/*
 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package com.sun.corba.se.impl.encoding;

import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.math.BigDecimal;
import java.util.LinkedList;

import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.ior.IOR;
import com.sun.corba.se.spi.ior.IORFactories;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.spi.logging.CORBALogDomains;
import com.sun.corba.se.spi.presentation.rmi.StubAdapter;
import com.sun.corba.se.spi.presentation.rmi.PresentationManager;
import com.sun.corba.se.spi.presentation.rmi.PresentationDefaults;

import com.sun.corba.se.impl.util.Utility;
import com.sun.corba.se.impl.orbutil.ORBUtility;
import com.sun.corba.se.impl.corba.TypeCodeImpl;
import com.sun.corba.se.impl.util.RepositoryId;
import com.sun.corba.se.impl.orbutil.ORBConstants;
import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.protocol.giopmsgheaders.Message;

import org.omg.CORBA.Any;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.Principal;
import org.omg.CORBA.portable.IDLEntity;

/**
 * Implementation class that uses Java serialization for input streams.
 * This assumes a GIOP version 1.2 message format.
 *
 * This class uses a ByteArrayInputStream as the underlying buffer. The
 * first 16 bytes are directly read out of the underlying buffer. This allows
 * [GIOPHeader (12 bytes) + requestID (4 bytes)] to be read as bytes.
 * Subsequent write operations on this output stream object uses
 * ObjectInputStream class to read into the buffer. This allows unmarshaling
 * complex types and graphs using the ObjectInputStream implementation.
 *
 * Note, this class assumes a GIOP 1.2 style header. Further, the first
 * 12 bytes, that is, the GIOPHeader is read directly from the received
 * message, before this stream object is called. So, this class effectively
 * reads only the requestID (4 bytes) directly, and uses the
 * ObjectInputStream for further unmarshaling.
 *
 * @author Ram Jeyaraman
 */
public class IDLJavaSerializationInputStream extends CDRInputStreamBase {

  private ORB orb;
  private int bufSize;
  private ByteBuffer buffer;
  private byte encodingVersion;
  private ObjectInputStream is;
  private _ByteArrayInputStream bis;
  private BufferManagerRead bufferManager;

  // [GIOPHeader(12) + requestID(4)] bytes
  private final int directReadLength = Message.GIOPMessageHeaderLength + 4;

  // Used for mark / reset operations.
  private boolean markOn;
  private int peekIndex, peekCount;
  private LinkedList markedItemQ = new LinkedList();

  protected ORBUtilSystemException wrapper;

  class _ByteArrayInputStream extends ByteArrayInputStream {

    _ByteArrayInputStream(byte[] buf) {
      super(buf);
    }

    int getPosition() {
      return this.pos;
    }

    void setPosition(int value) {
      if (value < 0 || value > count) {
        throw new IndexOutOfBoundsException();
      }
      this.pos = value;
    }
  }

  class MarshalObjectInputStream extends ObjectInputStream {

    ORB orb;

    MarshalObjectInputStream(java.io.InputStream out, ORB orb)
        throws IOException {

      super(out);
      this.orb = orb;

      java.security.AccessController.doPrivileged(
          new java.security.PrivilegedAction() {
            public Object run() {
              // needs SerializablePermission("enableSubstitution")
              enableResolveObject(true);
              return null;
            }
          }
      );
    }

    /**
     * Connect the Stub to the ORB.
     */
    protected final Object resolveObject(Object obj) throws IOException {
      try {
        if (StubAdapter.isStub(obj)) {
          StubAdapter.connect(obj, orb);
        }
      } catch (java.rmi.RemoteException re) {
        IOException ie = new IOException("resolveObject failed");
        ie.initCause(re);
        throw ie;
      }
      return obj;
    }
  }

  public IDLJavaSerializationInputStream(byte encodingVersion) {
    super();
    this.encodingVersion = encodingVersion;
  }

  public void init(org.omg.CORBA.ORB orb,
      ByteBuffer byteBuffer,
      int bufSize,
      boolean littleEndian,
      BufferManagerRead bufferManager) {
    this.orb = (ORB) orb;
    this.bufSize = bufSize;
    this.bufferManager = bufferManager;
    buffer = byteBuffer;
    wrapper =
        ORBUtilSystemException.get((ORB) orb, CORBALogDomains.RPC_ENCODING);

    byte[] buf;
    if (buffer.hasArray()) {
      buf = buffer.array();
    } else {
      buf = new byte[bufSize];
      buffer.get(buf);
    }
    // Note: at this point, the buffer position is zero. The setIndex()
    // method call can be used to set a desired read index.
    bis = new _ByteArrayInputStream(buf);
  }

  // Called from read_octet or read_long or read_ulong method.
  private void initObjectInputStream() {
    //System.out.print(" is ");
    if (is != null) {
      throw wrapper.javaStreamInitFailed();
    }
    try {
      is = new MarshalObjectInputStream(bis, orb);
    } catch (Exception e) {
      throw wrapper.javaStreamInitFailed(e);
    }
  }

  // org.omg.CORBA.portable.InputStream

  // Primitive types.

  public boolean read_boolean() {
    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return ((Boolean) markedItemQ.removeFirst()).booleanValue();
    }
    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return ((Boolean) markedItemQ.get(peekIndex++)).booleanValue();
    }
    try {
      boolean value = is.readBoolean();
      if (markOn) { // enqueue
        markedItemQ.addLast(Boolean.valueOf(value));
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_boolean");
    }
  }

  public char read_char() {
    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return ((Character) markedItemQ.removeFirst()).charValue();
    }
    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return ((Character) markedItemQ.get(peekIndex++)).charValue();
    }
    try {
      char value = is.readChar();
      if (markOn) { // enqueue
        markedItemQ.addLast(new Character(value));
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_char");
    }
  }

  public char read_wchar() {
    return this.read_char();
  }

  public byte read_octet() {

    // check if size < [ GIOPHeader(12) + requestID(4)] bytes
    if (bis.getPosition() < directReadLength) {
      byte b = (byte) bis.read();
      if (bis.getPosition() == directReadLength) {
        initObjectInputStream();
      }
      return b;
    }

    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return ((Byte) markedItemQ.removeFirst()).byteValue();
    }

    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return ((Byte) markedItemQ.get(peekIndex++)).byteValue();
    }

    try {
      byte value = is.readByte();
      if (markOn) { // enqueue
        //markedItemQ.addLast(Byte.valueOf(value)); // only in JDK 1.5
        markedItemQ.addLast(new Byte(value));
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_octet");
    }
  }

  public short read_short() {
    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return ((Short) markedItemQ.removeFirst()).shortValue();
    }
    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return ((Short) markedItemQ.get(peekIndex++)).shortValue();
    }

    try {
      short value = is.readShort();
      if (markOn) { // enqueue
        markedItemQ.addLast(new Short(value));
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_short");
    }
  }

  public short read_ushort() {
    return this.read_short();
  }

  public int read_long() {

    // check if size < [ GIOPHeader(12) + requestID(4)] bytes
    if (bis.getPosition() < directReadLength) {

      // Use big endian (network byte order). This is fixed.
      // Both the writer and reader use the same byte order.
      int b1 = (bis.read() << 24) & 0xFF000000;
      int b2 = (bis.read() << 16) & 0x00FF0000;
      int b3 = (bis.read() << 8) & 0x0000FF00;
      int b4 = (bis.read() << 0) & 0x000000FF;

      if (bis.getPosition() == directReadLength) {
        initObjectInputStream();
      } else if (bis.getPosition() > directReadLength) {
        // Cannot happen. All direct reads are contained
        // within the first 16 bytes.
        wrapper.javaSerializationException("read_long");
      }

      return (b1 | b2 | b3 | b4);
    }

    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return ((Integer) markedItemQ.removeFirst()).intValue();
    }

    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return ((Integer) markedItemQ.get(peekIndex++)).intValue();
    }

    try {
      int value = is.readInt();
      if (markOn) { // enqueue
        markedItemQ.addLast(new Integer(value));
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_long");
    }
  }

  public int read_ulong() {
    return this.read_long();
  }

  public long read_longlong() {
    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return ((Long) markedItemQ.removeFirst()).longValue();
    }
    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return ((Long) markedItemQ.get(peekIndex++)).longValue();
    }

    try {
      long value = is.readLong();
      if (markOn) { // enqueue
        markedItemQ.addLast(new Long(value));
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_longlong");
    }
  }

  public long read_ulonglong() {
    return read_longlong();
  }

  public float read_float() {
    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return ((Float) markedItemQ.removeFirst()).floatValue();
    }
    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return ((Float) markedItemQ.get(peekIndex++)).floatValue();
    }

    try {
      float value = is.readFloat();
      if (markOn) { // enqueue
        markedItemQ.addLast(new Float(value));
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_float");
    }
  }

  public double read_double() {
    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return ((Double) markedItemQ.removeFirst()).doubleValue();
    }
    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return ((Double) markedItemQ.get(peekIndex++)).doubleValue();
    }

    try {
      double value = is.readDouble();
      if (markOn) { // enqueue
        markedItemQ.addLast(new Double(value));
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_double");
    }
  }

  // String types.

  public String read_string() {
    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return (String) markedItemQ.removeFirst();
    }
    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return (String) markedItemQ.get(peekIndex++);
    }
    try {
      String value = is.readUTF();
      if (markOn) { // enqueue
        markedItemQ.addLast(value);
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_string");
    }
  }

  public String read_wstring() {
    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return (String) markedItemQ.removeFirst();
    }
    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return (String) markedItemQ.get(peekIndex++);
    }
    try {
      String value = (String) is.readObject();
      if (markOn) { // enqueue
        markedItemQ.addLast(value);
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_wstring");
    }
  }

  // Array types.

  public void read_boolean_array(boolean[] value, int offset, int length) {
    for (int i = 0; i < length; i++) {
      value[i + offset] = read_boolean();
    }
  }

  public void read_char_array(char[] value, int offset, int length) {
    for (int i = 0; i < length; i++) {
      value[i + offset] = read_char();
    }
  }

  public void read_wchar_array(char[] value, int offset, int length) {
    read_char_array(value, offset, length);
  }

  public void read_octet_array(byte[] value, int offset, int length) {
    for (int i = 0; i < length; i++) {
      value[i + offset] = read_octet();
    }
        /* // Cannot use this efficient read due to mark/reset support.
        try {
            while (length > 0) {
                int n = is.read(value, offset, length);
                offset += n;
                length -= n;
            }
        } catch (Exception e) {
            throw wrapper.javaSerializationException(e, "read_octet_array");
        }
        */
  }

  public void read_short_array(short[] value, int offset, int length) {
    for (int i = 0; i < length; i++) {
      value[i + offset] = read_short();
    }
  }

  public void read_ushort_array(short[] value, int offset, int length) {
    read_short_array(value, offset, length);
  }

  public void read_long_array(int[] value, int offset, int length) {
    for (int i = 0; i < length; i++) {
      value[i + offset] = read_long();
    }
  }

  public void read_ulong_array(int[] value, int offset, int length) {
    read_long_array(value, offset, length);
  }

  public void read_longlong_array(long[] value, int offset, int length) {
    for (int i = 0; i < length; i++) {
      value[i + offset] = read_longlong();
    }
  }

  public void read_ulonglong_array(long[] value, int offset, int length) {
    read_longlong_array(value, offset, length);
  }

  public void read_float_array(float[] value, int offset, int length) {
    for (int i = 0; i < length; i++) {
      value[i + offset] = read_float();
    }
  }

  public void read_double_array(double[] value, int offset, int length) {
    for (int i = 0; i < length; i++) {
      value[i + offset] = read_double();
    }
  }

  // Complex types.

  public org.omg.CORBA.Object read_Object() {
    return read_Object(null);
  }

  public TypeCode read_TypeCode() {
    TypeCodeImpl tc = new TypeCodeImpl(orb);
    tc.read_value(parent);
    return tc;
  }

  public Any read_any() {

    Any any = orb.create_any();
    TypeCodeImpl tc = new TypeCodeImpl(orb);

    // read off the typecode

    // REVISIT We could avoid this try-catch if we could peek the typecode
    // kind off this stream and see if it is a tk_value.
    // Looking at the code we know that for tk_value the Any.read_value()
    // below ignores the tc argument anyway (except for the kind field).
    // But still we would need to make sure that the whole typecode,
    // including encapsulations, is read off.
    try {
      tc.read_value(parent);
    } catch (org.omg.CORBA.MARSHAL ex) {
      if (tc.kind().value() != org.omg.CORBA.TCKind._tk_value) {
        throw ex;
      }
      // We can be sure that the whole typecode encapsulation has been
      // read off.
      ex.printStackTrace();
    }

    // read off the value of the any.
    any.read_value(parent, tc);

    return any;
  }

  public Principal read_Principal() {
    // We don't need an implementation for this method, since principal
    // is absent in GIOP version 1.2 or above.
    int len = read_long();
    byte[] pvalue = new byte[len];
    read_octet_array(pvalue, 0, len);
    Principal p = new com.sun.corba.se.impl.corba.PrincipalImpl();
    p.name(pvalue);
    return p;
  }

  public BigDecimal read_fixed() {
    return new BigDecimal(read_fixed_buffer().toString());
  }

  // Each octet contains (up to) two decimal digits. If the fixed type has
  // an odd number of decimal digits, then the representation
  // begins with the first (most significant) digit.
  // Otherwise, this first half-octet is all zero, and the first digit
  // is in the second half-octet.
  // The sign configuration, in the last half-octet of the representation,
  // is 0xD for negative numbers and 0xC for positive and zero values.
  private StringBuffer read_fixed_buffer() {
    StringBuffer buffer = new StringBuffer(64);
    byte doubleDigit;
    int firstDigit;
    int secondDigit;
    boolean wroteFirstDigit = false;
    boolean more = true;
    while (more) {
      doubleDigit = read_octet();
      firstDigit = (int) ((doubleDigit & 0xf0) >> 4);
      secondDigit = (int) (doubleDigit & 0x0f);
      if (wroteFirstDigit || firstDigit != 0) {
        buffer.append(Character.forDigit(firstDigit, 10));
        wroteFirstDigit = true;
      }
      if (secondDigit == 12) {
        // positive number or zero
        if (!wroteFirstDigit) {
          // zero
          return new StringBuffer("0.0");
        } else {
          // positive number
          // done
        }
        more = false;
      } else if (secondDigit == 13) {
        // negative number
        buffer.insert(0, '-');
        more = false;
      } else {
        buffer.append(Character.forDigit(secondDigit, 10));
        wroteFirstDigit = true;
      }
    }
    return buffer;
  }

  public org.omg.CORBA.Object read_Object(java.lang.Class clz) {

    // In any case, we must first read the IOR.
    IOR ior = IORFactories.makeIOR(parent);
    if (ior.isNil()) {
      return null;
    }

    PresentationManager.StubFactoryFactory sff =
        ORB.getStubFactoryFactory();
    String codeBase = ior.getProfile().getCodebase();
    PresentationManager.StubFactory stubFactory = null;

    if (clz == null) {
      RepositoryId rid = RepositoryId.cache.getId(ior.getTypeId());
      String className = rid.getClassName();
      boolean isIDLInterface = rid.isIDLType();

      if (className == null || className.equals("")) {
        stubFactory = null;
      } else {
        try {
          stubFactory = sff.createStubFactory(className,
              isIDLInterface, codeBase, (Class) null,
              (ClassLoader) null);
        } catch (Exception exc) {
          // Could not create stubFactory, so use null.
          // XXX stubFactory handling is still too complex:
          // Can we resolve the stubFactory question once in
          // a single place?
          stubFactory = null;
        }
      }
    } else if (StubAdapter.isStubClass(clz)) {
      stubFactory = PresentationDefaults.makeStaticStubFactory(clz);
    } else {
      // clz is an interface class
      boolean isIDL = IDLEntity.class.isAssignableFrom(clz);

      stubFactory = sff.createStubFactory(
          clz.getName(), isIDL, codeBase, clz, clz.getClassLoader());
    }

    return CDRInputStream_1_0.internalIORToObject(ior, stubFactory, orb);
  }

  public org.omg.CORBA.ORB orb() {
    return this.orb;
  }

  // org.omg.CORBA_2_3.portable.InputStream

  public java.io.Serializable read_value() {
    if (!markOn && !(markedItemQ.isEmpty())) { // dequeue
      return (Serializable) markedItemQ.removeFirst();
    }
    if (markOn && !(markedItemQ.isEmpty()) &&
        (peekIndex < peekCount)) { // peek
      return (Serializable) markedItemQ.get(peekIndex++);
    }
    try {
      Serializable value = (java.io.Serializable) is.readObject();
      if (markOn) { // enqueue
        markedItemQ.addLast(value);
      }
      return value;
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "read_value");
    }
  }

  public java.io.Serializable read_value(java.lang.Class clz) {
    return read_value();
  }

  public java.io.Serializable read_value(
      org.omg.CORBA.portable.BoxedValueHelper factory) {
    return read_value();
  }

  public java.io.Serializable read_value(java.lang.String rep_id) {
    return read_value();
  }

  public java.io.Serializable read_value(java.io.Serializable value) {
    return read_value();
  }

  public java.lang.Object read_abstract_interface() {
    return read_abstract_interface(null);
  }

  public java.lang.Object read_abstract_interface(java.lang.Class clz) {
    boolean isObject = read_boolean();
    if (isObject) {
      return read_Object(clz);
    } else {
      return read_value();
    }
  }

  // com.sun.corba.se.impl.encoding.MarshalInputStream
  public void consumeEndian() {
    throw wrapper.giopVersionError();
  }

  public int getPosition() {
    try {
      return bis.getPosition();
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "getPosition");
    }
  }

  // org.omg.CORBA.DataInputStream
  public java.lang.Object read_Abstract() {
    return read_abstract_interface();
  }

  public java.io.Serializable read_Value() {
    return read_value();
  }

  public void read_any_array(org.omg.CORBA.AnySeqHolder seq,
      int offset, int length) {
    read_any_array(seq.value, offset, length);
  }

  private final void read_any_array(org.omg.CORBA.Any[] value,
      int offset, int length) {
    for (int i = 0; i < length; i++) {
      value[i + offset] = read_any();
    }
  }

  public void read_boolean_array(org.omg.CORBA.BooleanSeqHolder seq,
      int offset, int length) {
    read_boolean_array(seq.value, offset, length);
  }

  public void read_char_array(org.omg.CORBA.CharSeqHolder seq,
      int offset, int length) {
    read_char_array(seq.value, offset, length);
  }

  public void read_wchar_array(org.omg.CORBA.WCharSeqHolder seq,
      int offset, int length) {
    read_wchar_array(seq.value, offset, length);
  }

  public void read_octet_array(org.omg.CORBA.OctetSeqHolder seq,
      int offset, int length) {
    read_octet_array(seq.value, offset, length);
  }

  public void read_short_array(org.omg.CORBA.ShortSeqHolder seq,
      int offset, int length) {
    read_short_array(seq.value, offset, length);
  }

  public void read_ushort_array(org.omg.CORBA.UShortSeqHolder seq,
      int offset, int length) {
    read_ushort_array(seq.value, offset, length);
  }

  public void read_long_array(org.omg.CORBA.LongSeqHolder seq,
      int offset, int length) {
    read_long_array(seq.value, offset, length);
  }

  public void read_ulong_array(org.omg.CORBA.ULongSeqHolder seq,
      int offset, int length) {
    read_ulong_array(seq.value, offset, length);
  }

  public void read_ulonglong_array(org.omg.CORBA.ULongLongSeqHolder seq,
      int offset, int length) {
    read_ulonglong_array(seq.value, offset, length);
  }

  public void read_longlong_array(org.omg.CORBA.LongLongSeqHolder seq,
      int offset, int length) {
    read_longlong_array(seq.value, offset, length);
  }

  public void read_float_array(org.omg.CORBA.FloatSeqHolder seq,
      int offset, int length) {
    read_float_array(seq.value, offset, length);
  }

  public void read_double_array(org.omg.CORBA.DoubleSeqHolder seq,
      int offset, int length) {
    read_double_array(seq.value, offset, length);
  }

  // org.omg.CORBA.portable.ValueBase

  public String[] _truncatable_ids() {
    throw wrapper.giopVersionError();
  }

  // java.io.InputStream
  // REVISIT - should we make these throw UnsupportedOperationExceptions?
  // Right now, they'll go up to the java.io versions!

  //     public int read(byte b[]) throws IOException;
  //     public int read(byte b[], int off, int len) throws IOException
  //     public long skip(long n) throws IOException;
  //     public int available() throws IOException;
  //     public void close() throws IOException;

  public void mark(int readLimit) {
    // Nested mark disallowed.
    // Further, mark is not supported until first 16 bytes are read.
    if (markOn || is == null) {
      throw wrapper.javaSerializationException("mark");
    }
    markOn = true;
    if (!(markedItemQ.isEmpty())) {
      peekIndex = 0;
      peekCount = markedItemQ.size();
    }
        /*
        // Note: only ByteArrayInputStream supports mark/reset.
        if (is == null || is.markSupported() == false) {
            throw wrapper.javaSerializationException("mark");
        }
        is.mark(readLimit);
        */
  }

  public void reset() {
    markOn = false;
    peekIndex = 0;
    peekCount = 0;
        /*
        // Note: only ByteArrayInputStream supports mark/reset.
        if (is == null || is.markSupported() == false) {
            throw wrapper.javaSerializationException("mark");
        }
        try {
            is.reset();
        } catch (Exception e) {
            throw wrapper.javaSerializationException(e, "reset");
        }
        */
  }

  // This should return false so that outside users (people using the JDK)
  // don't have any guarantees that mark/reset will work in their
  // custom marshaling code.  This is necessary since they could do things
  // like expect obj1a == obj1b in the following code:
  //
  // is.mark(10000);
  // Object obj1a = is.readObject();
  // is.reset();
  // Object obj1b = is.readObject();
  //
  public boolean markSupported() {
    return true;
  }

  // Needed by AnyImpl and ServiceContexts
  public CDRInputStreamBase dup() {

    CDRInputStreamBase result = null;

    try {
      result = (CDRInputStreamBase) this.getClass().newInstance();
    } catch (Exception e) {
      throw wrapper.couldNotDuplicateCdrInputStream(e);
    }

    result.init(this.orb, this.buffer, this.bufSize, false, null);

    // Set the buffer position.
    ((IDLJavaSerializationInputStream) result).skipBytes(getPosition());

    // Set mark related data.
    ((IDLJavaSerializationInputStream) result).
        setMarkData(markOn, peekIndex, peekCount,
            (LinkedList) markedItemQ.clone());

    return result;
  }

  // Used exclusively by the dup() method.
  void skipBytes(int len) {
    try {
      is.skipBytes(len);
    } catch (Exception e) {
      throw wrapper.javaSerializationException(e, "skipBytes");
    }
  }

  // Used exclusively by the dup() method.
  void setMarkData(boolean markOn, int peekIndex, int peekCount,
      LinkedList markedItemQ) {
    this.markOn = markOn;
    this.peekIndex = peekIndex;
    this.peekCount = peekCount;
    this.markedItemQ = markedItemQ;
  }

  // Needed by TCUtility
  public java.math.BigDecimal read_fixed(short digits, short scale) {
    // digits isn't really needed here
    StringBuffer buffer = read_fixed_buffer();
    if (digits != buffer.length()) {
      throw wrapper.badFixed(new Integer(digits),
          new Integer(buffer.length()));
    }
    buffer.insert(digits - scale, '.');
    return new BigDecimal(buffer.toString());
  }

  // Needed by TypeCodeImpl
  public boolean isLittleEndian() {
    throw wrapper.giopVersionError();
  }

  // Needed by request and reply messages for GIOP versions >= 1.2 only.
  void setHeaderPadding(boolean headerPadding) {
    // no-op. We don't care about body alignment while using
    // Java serialization. What the GIOP spec states does not apply here.
  }

  // Needed by IIOPInputStream and other subclasses

  public ByteBuffer getByteBuffer() {
    throw wrapper.giopVersionError();
  }

  public void setByteBuffer(ByteBuffer byteBuffer) {
    throw wrapper.giopVersionError();
  }

  public void setByteBufferWithInfo(ByteBufferWithInfo bbwi) {
    throw wrapper.giopVersionError();
  }

  public int getBufferLength() {
    return bufSize;
  }

  public void setBufferLength(int value) {
    // this is redundant, since buffer size was already specified
    // as part of the init call. So, ignore.
  }

  public int getIndex() {
    return bis.getPosition();
  }

  public void setIndex(int value) {
    try {
      bis.setPosition(value);
    } catch (IndexOutOfBoundsException e) {
      throw wrapper.javaSerializationException(e, "setIndex");
    }
  }

  public void orb(org.omg.CORBA.ORB orb) {
    this.orb = (ORB) orb;
  }

  public BufferManagerRead getBufferManager() {
    return bufferManager;
  }

  public GIOPVersion getGIOPVersion() {
    return GIOPVersion.V1_2;
  }

  com.sun.org.omg.SendingContext.CodeBase getCodeBase() {
    return parent.getCodeBase();
  }

  void printBuffer() {
    byte[] buf = this.buffer.array();

    System.out.println("+++++++ Input Buffer ++++++++");
    System.out.println();
    System.out.println("Current position: " + getPosition());
    System.out.println("Total length : " + this.bufSize);
    System.out.println();

    char[] charBuf = new char[16];

    try {

      for (int i = 0; i < buf.length; i += 16) {

        int j = 0;

        // For every 16 bytes, there is one line
        // of output.  First, the hex output of
        // the 16 bytes with each byte separated
        // by a space.
        while (j < 16 && j + i < buf.length) {
          int k = buf[i + j];
          if (k < 0) {
            k = 256 + k;
          }
          String hex = Integer.toHexString(k);
          if (hex.length() == 1) {
            hex = "0" + hex;
          }
          System.out.print(hex + " ");
          j++;
        }

        // Add any extra spaces to align the
        // text column in case we didn't end
        // at 16
        while (j < 16) {
          System.out.print("   ");
          j++;
        }

        // Now output the ASCII equivalents.  Non-ASCII
        // characters are shown as periods.
        int x = 0;

        while (x < 16 && x + i < buf.length) {
          if (ORBUtility.isPrintable((char) buf[i + x])) {
            charBuf[x] = (char) buf[i + x];
          } else {
            charBuf[x] = '.';
          }
          x++;
        }
        System.out.println(new String(charBuf, 0, x));
      }
    } catch (Throwable t) {
      t.printStackTrace();
    }
    System.out.println("++++++++++++++++++++++++++++++");
  }

  void alignOnBoundary(int octetBoundary) {
    throw wrapper.giopVersionError();
  }

  void performORBVersionSpecificInit() {
    // No-op.
  }

  public void resetCodeSetConverters() {
    // No-op.
  }

  // ValueInputStream -------------------------

  public void start_value() {
    throw wrapper.giopVersionError();
  }

  public void end_value() {
    throw wrapper.giopVersionError();
  }
}
